import { _decorator, AnimationClip, Camera, CCInteger, clamp, Color, Component, game, Graphics, macro, Node, NodeEventType, Rect, resources, SpriteFrame, toDegree, Touch, tween, UITransform, Vec2, view } from 'cc';
import { Player } from './Player/Player';
import { GameUtils, MyMath } from '../utils';
import { monsterManager } from '../manager/MonsterManager';
import { CUSTOM_EVENT, dispatcher } from '../manager/EventDispatcher';
import { MonsterController } from './MonsterController';
const { ccclass, property } = _decorator;
const LOG_TITLE = 'Game'

//碰撞相关逻辑
//monster:
//monster相当于是在主节点空间下
//Player:
//根节点只负责做移动
//本来想着子节点Creature负责做旋转
//但是做起碰撞检测时候很麻烦 (在碰撞检测3的时候，需要将creature子节点放在Player节点空间下，boundingbox还需要另做坐标和旋转变换；如果直接在节点左旋转拿到的boundingbox就是Player节点坐标系下的变换，Creature根节点不要做任何变换)

//碰撞检测
//1、先判断monster是否在Player大的节点内部   是否在玩家视野内
//2、如果在视野内 monster是否在Player下整个Creature的AABB框内  (需要将monster的碰撞框转到Player的节点空间下)
//3、如果是，则用monster和Creature的子节点的aabb框进行碰撞检测  (需要将onster的碰撞框转到Player的节点空间下， creature子节点也在Player下)
//4、检测过，再进行旋转矩形线段相交判断 (同上)

const Boundary = new Rect(-2000, -2000, 4000, 4000)

const SPECIAL_DURATION = 60

@ccclass('Game')
export class Game extends Component {
    @property(Node)
    public nodePlayer: Node = null

    @property(Node)
    public nodeMonsters: Node = null

    @property(Graphics)
    public debugDrawMonster: Graphics = null

    @property(Camera)
    public attachCamera: Camera = null

    @property(Camera)
    public attachCamera2: Camera = null

    @property(CCInteger)
    public spawnInterval: number = 5

    private _player: Player = null

    private _monsterCollection: Node[] = null
    private _monsterRects: Rect[] = []

    private _tempDestroyMonsterNode: Node[] = []
    // private _monsterManager: MonsterManager = null
    private _monsterAreas: Node[][] = []

    private _originOrthoHeight: number = 0

    private _spawnTime: number = Date.now() + 2000 //产生怪物

    private _pause: boolean = true  //游戏是否是暂停状态
    private _start: boolean = false //加载完资源标记游戏开始

    private _specialTime: number = undefined //神坤时刻终结时间

    onLoad() {
        this.nodePlayer.active = false
        this._player = this.nodePlayer.getComponent(Player)

        monsterManager.nodeMonsters = this.nodeMonsters
        
        this._originOrthoHeight = this.attachCamera.orthoHeight
        this._monsterCollection = this.nodeMonsters.children
        
        this._monsterAreas.length = 9
        this._monsterAreas.fill(undefined)
        this._monsterAreas.forEach((area, index) => this._monsterAreas[index] = [])


        resources.loadDir('ui/creature', SpriteFrame, () => {
            resources.loadDir('anim/', AnimationClip, () => { //start game
                this.startGame()
            })
        })
    }

    onEnable() {
        dispatcher.on(CUSTOM_EVENT.CHANGE, this.onChange, this)
    }

    onDisable() {
        dispatcher.off(CUSTOM_EVENT.CHANGE, this.onChange, this)
    }

    startGame() {
        this._start = true
        this._pause = false
        this.nodePlayer.active = true
    }

    update(dt: number): void {
        if (this._pause)
            return

        // console.log(this.attachCamera.orthoHeight, this.attachCamera2.orthoHeight, view.getVisibleSize().height)
        this.debugDrawMonster.clear()
        this.debugDrawMonster.lineWidth = 4

        this._monsterRects.length = 0
        this._monsterRects.length = this._monsterCollection.length
        this._tempDestroyMonsterNode.length = 0

        //move rotate
        this._player.updateOuter(dt)
        this._monsterCollection.forEach(monster => monster.getComponent(MonsterController).updateOuter(dt))

        //boundary detect
        this._player.node.setPosition(clamp(this._player.node.position.x,  Boundary.xMin, Boundary.xMax), clamp(this._player.node.position.y,  Boundary.yMin, Boundary.yMax))
        this._monsterCollection.forEach(monster => monster.setPosition(clamp(monster.position.x,  Boundary.xMin, Boundary.xMax), clamp(monster.position.y,  Boundary.yMin, Boundary.yMax)))

        this.classifyMonsters()
        this.detectCollisions()
        this._player.collisionSettle()
        this.monstersCollisions()
        this._tempDestroyMonsterNode.forEach(node => monsterManager.pushMonster(node))

        //updateMonster
        this.spawnMonsters()
        this.checkSpecialOver()
    }   

    classifyMonsters() {
        //根据当前playerRect 将monster分为9个区域
        const playerRect = this.nodePlayer.getComponent(UITransform).getBoundingBox()
        const xMin = playerRect.xMin
        const xMax = playerRect.xMax
        const yMin = playerRect.yMin
        const yMax = playerRect.yMax

        this._monsterAreas.forEach(area => area.length = 0)
        const _tempArrX = []
        const _tempArrY = []
        const func = (min, max, isX) => {
            const _max = isX ? xMax : yMax
            const _min = isX ? xMin : yMin
            const arr = isX ? _tempArrX : _tempArrY
            arr.length = 0
            if (min >= _max) { //右上 3
                arr.push(0)
            }
            else if (max <= _min) { //右下 9
                arr.push(2)
            }
            else if (max < _max && min > _min) { //右中
                arr.push(1)
            }
            else {
                if (max > _max) { //右上 + 右中
                    arr.push(0)
                    arr.push(1)
                }
                else {
                    arr.push(1)
                    arr.push(2)
                }
            }
        }

        for (let i = 0; i < this._monsterCollection.length; i++) {
            const monster = this._monsterCollection[i]
            const rect = this.getMonsterRect(i)
            func(rect.xMin, rect.xMax, true)
            func(rect.yMin, rect.yMax, false)
            _tempArrX.forEach(posX => {
                _tempArrY.forEach(posY => {
                    this._monsterAreas[posX * 3 + posY].push(monster)
                })
            })
        }
    }

    detectCollisions() {
        const playerRect = this.nodePlayer.getComponent(UITransform).getBoundingBox()
        const monsters = this._monsterAreas[4]
        for (let i = 0; i < monsters.length; i++) {
            const monster = monsters[i]
            const lv = Number(monster.name)
            if (this.isMonsterInPlayerRect(playerRect, monster)) {
                const rect = this.getMonsterRect(monster.getSiblingIndex())

                //debugdraw
                this.debugDrawMonster.rect(rect.x, rect.y, rect.width, rect.height)
                this.debugDrawMonster.strokeColor = Color.BLACK
                this.debugDrawMonster.stroke()
                
                const creatureRectLines = GameUtils.getNodeRectLines(monster)
                this.debugDrawMonster.moveTo(creatureRectLines[0][0].x, creatureRectLines[0][0].y)
                this.debugDrawMonster.lineTo(creatureRectLines[1][0].x, creatureRectLines[1][0].y)
                this.debugDrawMonster.lineTo(creatureRectLines[2][0].x, creatureRectLines[2][0].y)
                this.debugDrawMonster.lineTo(creatureRectLines[3][0].x, creatureRectLines[3][0].y)
                this.debugDrawMonster.close()

                //convert to node player space
                rect.x -= this.nodePlayer.position.x
                rect.y -= this.nodePlayer.position.y

                if (this._player.isMonsterInCreatureRect(rect)) { //与整个aabb框判断碰撞
                    const creatures = this._player.getMonsterIntersectCreatureIds(rect, monster) //与单个creature进行碰撞检测
                    if (creatures.length > 0) {
                        let isSwallow = false
                        creatures.forEach(id => {
                            if (this._player.monsterCollision(lv, id)) {
                                isSwallow = true
                            }
                        })
                        this.debugDrawMonster.strokeColor = Color.YELLOW

                        if (isSwallow) {
                            this._tempDestroyMonsterNode.push(monster)
                        }
                    }
                }
                else {
                    this.debugDrawMonster.strokeColor = Color.RED
                }
                this.debugDrawMonster.stroke()
            }
        }
    }

    monstersCollisions() {
        this._monsterAreas.forEach(area => {
            area.forEach(monster => {
                if (this._tempDestroyMonsterNode.indexOf(monster) == -1) {
                    const rect = this.getMonsterRect(monster.getSiblingIndex())
                    const monsterRectLines = GameUtils.getNodeRectLines(monster)
                    const lv = Number(monster.name)
                    area.find(anotherMonster => {
                        if (monster != anotherMonster && this._tempDestroyMonsterNode.indexOf(anotherMonster) == -1) {
                            if (rect.intersects(this.getMonsterRect(anotherMonster.getSiblingIndex()))) {
                                if (MyMath.isRectIntersect(monsterRectLines, GameUtils.getNodeRectLines(anotherMonster))) {
                                    console.log(LOG_TITLE, ' monster 相撞了 ')
                                    const anotherLv = Number(anotherMonster.name)
                                    if (lv > anotherLv) { //等级相同不碰撞
                                        this._tempDestroyMonsterNode.push(anotherMonster)
                                    }
                                    else if (lv < anotherLv) {
                                        this._tempDestroyMonsterNode.push(monster)
                                        return true
                                    }
                                }
                            }
                        }
                    })
                }
            })
        })
    }

    spawnMonsters(force = false) {
        const time = Date.now()
        const isSpecial = !!this._specialTime
        
        if (this._spawnTime < time || force) {
            const range = new Vec2()
            const scale = view.getVisibleSize().width / view.getVisibleSize().height
            range.y = this.attachCamera.orthoHeight
            range.x = scale * range.y
            monsterManager.updateEnvironment(this._player.getMaxLv(), this._player.node.position, range, Boundary, isSpecial)
            const interval = this.spawnInterval as number;
            this._spawnTime = Date.now() + interval * 1000
        }

        // this.debugDrawMonster.rect(this._player.node.position.x - range.x + 100, this._player.node.position.y - range.y + 100, range.x * 2 - 200, range.y * 2 - 200)
        // this.debugDrawMonster.stroke()
    }

    spawnFollowMonster(lv = 1) {
        const monster = monsterManager.popMonster(lv)
        if (monster) {
            const playerPos = this._player.node.position
            monster.setPosition(playerPos.x + 400, playerPos.y + 400) //todo toward朝向
            const controller = monster.getComponent(MonsterController)
            controller.setFollowTarget(this.nodePlayer)
            this.nodeMonsters.addChild(monster)
            controller.data_speed = 500
        }
        else {
            console.error(LOG_TITLE, '无法生成monter Lv:', lv)
        }
    }

    isMonsterInPlayerRect(playerRect: Rect, monster: Node) { //monster 是否在视野范围内
        const monsterRect = this.getMonsterRect(monster.getSiblingIndex())
        if (monsterRect) {
            const isIntersect = playerRect.intersects(monsterRect)

            //debug
            if (isIntersect) {
                // console.log(LOG_TITLE, ' monster in player rect: ', monster.name)
            }

            return isIntersect
        }
    }

    getMonsterRect(monsterIndex: number) {
        let rect = this._monsterRects[monsterIndex]
        if (!rect) {
            const monster = this._monsterCollection[monsterIndex]
            if (!monster) {
                console.error(LOG_TITLE, ' 无法索引到monster:', monsterIndex, this._monsterCollection)
                return
            }

            rect = this._monsterCollection[monsterIndex].getComponent(UITransform).getBoundingBox()
            this._monsterRects[monsterIndex] = rect
        }

        return rect
    }

    onChange(lv: number) { //退化或者进化
        const orthoHeight = lv * 20 + this._originOrthoHeight
        tween() 
        .target(this.attachCamera)
        .to(0.3, {
            orthoHeight : orthoHeight
        })
        .start()
    }

    evolutionPlayer() {
        this._player.evolution()
    }

    //special++++++++++
    startSpecial() {
        const isSpecial = !!this._specialTime
        this._specialTime = Date.now() + SPECIAL_DURATION * 1000
    
        if (!isSpecial) {
            //destory all monster
            for (let i = this._monsterCollection.length - 1 ;  i >= 0; i--) {
                const monster = this._monsterCollection[0]
                monsterManager.pushMonster(monster)
            }
            this.spawnMonsters(true)

            //player start special effect
            this._player.startSpecial()
        }
    }

    checkSpecialOver() {
        if (this._specialTime && this._specialTime < Date.now()) {
            this._specialTime = undefined
            this._player.stopSpecial()
            //clear all monster
            for (let i = this._monsterCollection.length - 1 ;  i >= 0; i--) {
                const monster = this._monsterCollection[0]
                monsterManager.pushMonster(monster)
            }
            this.spawnMonsters(true)
        }
    }

    //special----------
    flyForward() {
        this._player.flyForward()
    }

    //bridge ui
    getPlayerMaxLv() {
        return this._start ? this._player.getMaxLv() : 0
    }


    getMonsterInfos() {
        const children = this.nodeMonsters.children
        const monsterLvNums = {}
        children.forEach(child => {
            monsterLvNums[child.name] = (monsterLvNums[child.name] || 0) + 1
        })

        const config = monsterManager.getMonsterConfigs()

        return {nums: monsterLvNums, config: config}
    }

    getSpawnInfo() {
        return {spawnTime : this._spawnTime - Date.now(), interval: this.spawnInterval}
    }

    getSpecialTime() {
        return this._specialTime
    }

    //global
    gamePause() {
        this._pause = true
    }

    gameResume() {
        this._pause = false
    }

    //temp
    toggleView(height?: number) {
        if (this.attachCamera.orthoHeight == 4000) {
            this.attachCamera.orthoHeight = this._player.getMaxLv() * 20 + this._originOrthoHeight
        }
        else {
            this.attachCamera.orthoHeight = 4000
        }
    }
}


